package com.cnwidsom.monitor;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import net.sf.json.JSONObject;

/**
 * 所有调用关系都是由Token定义,每次调用都生成一个新的token,并且记录和调用者的关系
 * 
 * 
 */
public class Token implements Serializable {
	public static final int TYPE_AUTO = -1;
	public static final int TYPE_START_CALL = 0;
	public static final int TYPE_NONSTART_CALL = 1;
	public static final int TYPE_CALLED = 2;
	public static final int TYPE_DISTRIBUTE_CALLED = 3;
	public static final int TYPE_DISTRIBUTE_CALL = 4;
	public static final int TYPE_DB_CALLED = 5;
	public static final int TYPE_NOP = 6;// 占位使用

	// token指针,每次都指向当前调用位置的token
	private static final ThreadLocal<Token> token_header = new ThreadLocal<Token>();
	private static final ThreadLocal<List<Token>> token_list = new ThreadLocal<List<Token>>();
	private String curTokenValue;
	private Token previousToken;
	private List<Token> nextToken = new ArrayList<Token>();
	private int type;
	private String name;// 名称,用于显示
	private Object extraData;// 用于存储附加信息
	private boolean isDistribute = false;// 是否为分布式调用(被调用端使用)
	private boolean isStartToken = false;// 是否为起始Token
	private boolean notRecord = false;// 不在日志记录

	private long startTime = 0;// 创建时间

	private Token(String startToken, String name) {
		this(startToken, TYPE_START_CALL, name);
	}

	private Token(String startToken, int type, String name) {
		this.startTime = System.currentTimeMillis();
		this.curTokenValue = startToken;
		this.type = type;
		this.name = name;
	}

	private static void addTokenToList(Token token, boolean isInit) {
		if (isInit)
			token_list.set(new ArrayList<Token>());
		token_list.get().add(token);
		// System.out.println("addToken[" + token_list.get().size() + "]:" +
		// token);
	}

	private static int getStartType() {
		List<Token> tlist = token_list.get();
		if (tlist != null && tlist.size() > 0) {
			return tlist.get(0).type;
		}
		return Token.TYPE_AUTO;
	}

	/**
	 * 空Token,做占位使用
	 * 
	 * @return
	 */
	public static Token createNopToken() {
		Token nopToken = new Token("NOP", "NONAME");
		nopToken.notRecord = true;
		nopToken.type = TYPE_NOP;
		if (token_header.get() == null) {
			token_header.set(nopToken);
		} else {
			nopToken.previousToken = token_header.get();
			token_header.get().nextToken.add(nopToken);
			token_header.set(nopToken);
		}
		return nopToken;
	}

	/**
	 * 接收远程调用传递的Token,并以此做为起始Token
	 * 
	 * @param remoteToken
	 * @return
	 */
	public static Token distributeToken(Token remoteToken) {
		Token startToken = remoteToken;
		if (remoteToken != null) {
			startToken.isDistribute = true;
			startToken.isStartToken = false;
			startToken.type = TYPE_DISTRIBUTE_CALLED;
			startToken.previousToken.isStartToken = true;
			startToken.previousToken.type = TYPE_DISTRIBUTE_CALLED;
			token_header.set(startToken);
			addTokenToList(startToken, true);
		}
		return startToken;
	}

	public static Token distributeToken(String name, String curTokenStr, String prevTokenStr) {
		Token startToken = new Token(curTokenStr, TYPE_DISTRIBUTE_CALLED, name);
		startToken.isDistribute = true;
		startToken.isStartToken = false;
		Token prevToken = new Token(prevTokenStr, TYPE_CALLED, name + "-prev");
		prevToken.isStartToken = true;
		prevToken.type = TYPE_DISTRIBUTE_CALLED;
		startToken.previousToken = prevToken;

		token_header.set(startToken);
		addTokenToList(startToken, true);
		return startToken;
	}

	/**
	 * 创建新token,一般在起始方法处使用
	 * 
	 * @return 返回新创建的token
	 */
	public static Token newToken(String name) {
		Token startToken = new Token(UUID.randomUUID().toString(), name);
		startToken.isStartToken = true;

		switch (getStartType()) {
		case Token.TYPE_NONSTART_CALL:// 如果队列头是NONSTART,那么以此调用为起始
			addTokenToList(startToken, true);
			break;
		case Token.TYPE_START_CALL:// 如果对列头是START,那么本次调用视为CALLED
			startToken = createNextToken(name, Token.TYPE_CALLED, true);
			startToken.isStartToken = false;
			break;
		case Token.TYPE_DISTRIBUTE_CALLED:// 如果对列头是DISTRIBUTE_CALLED,那么本次调用视为CALLED
			startToken = createNextToken(name, Token.TYPE_CALLED, true);
			startToken.isStartToken = false;
			break;
		case Token.TYPE_AUTO:// 如果没有队列头,那么以此调用为起始
			addTokenToList(startToken, true);
			break;
		}
		token_header.set(startToken);
		return startToken;
	}

	/**
	 * 创建下一级调用token,并且将header指向最新创建的token
	 * 
	 * @return
	 */
	public static Token createNextToken(String name, int type, boolean isRecordOnChain) {
		Token nextToken = null;
		if (token_header.get() == null) {
			if (type == TYPE_DISTRIBUTE_CALLED) {
				nextToken = new Token(UUID.randomUUID().toString(), type, name);
			} else {
				if (type == TYPE_AUTO)
					nextToken = new Token(UUID.randomUUID().toString(), TYPE_NONSTART_CALL, name);
				else
					nextToken = new Token(UUID.randomUUID().toString(), type, name);
			}
			nextToken.isStartToken = true;
			token_header.set(nextToken);
		} else {
			if (type == TYPE_AUTO)
				type = TYPE_CALLED;
			nextToken = new Token(UUID.randomUUID().toString(), type, name);
			nextToken.previousToken = token_header.get();
			token_header.get().nextToken.add(nextToken);
			token_header.set(nextToken);
		}

		if (token_list.get() == null) {
			token_list.set(new ArrayList<Token>());
		}

		if (!isRecordOnChain) {
			nextToken.notRecord = true;
		}
		token_list.get().add(nextToken);
		// System.out.println("addToken[" + token_list.get().size() + "]:" +
		// nextToken);
		return nextToken;
	}

	public static Token createNextToken(String name) {
		return createNextToken(name, TYPE_AUTO, true);
	}

	/**
	 * 获得当前的Header
	 * 
	 * @return
	 */
	public static Token getTokenHeader() {
		return token_header.get();
	}

	/**
	 * 结束调用,header指向上一个token
	 * 
	 * @return
	 */
	public static Token endBackToken() {
		return endBackToken(null);
	}

	/**
	 * 与endBackToken()类似，不过会传入一个附加数据保存到当前token中
	 * 
	 * @return
	 */
	public static Token endBackToken(Object extraData) {
		return endBackToken(extraData, false);
	}

	/**
	 * 与endBackToken()类似，不过会传入一个附加数据保存到当前token中
	 * 
	 * @param extraData
	 * @param untilStart
	 *            如果当前token被标记为start,则不向上查找
	 * @return
	 */
	public static Token endBackToken(Object extraData, boolean untilStart) {
		if (untilStart && token_header.get() != null && token_header.get().isStartToken) {
			token_header.get().setExtraData(extraData);
		} else {
			if (token_header.get() != null) {
				token_header.get().setExtraData(extraData);
				token_header.set(token_header.get().previousToken);
			}
		}
		return token_header.get();
	}

	/**
	 * 调用链结束后用此方法释放之前记录的调用关系表
	 * <p>
	 * <b>函数会自动监测是否已经到达起始调用位置,如果没有到达,则返回空,并且不会清空关系表</b>
	 * </p>
	 * 
	 * @return 返回之前记录的调用关系表
	 */
	public static List<Token> releaseToken() {
		if (token_header.get() != null && (token_header.get().getType() != Token.TYPE_DISTRIBUTE_CALLED
				|| (!token_header.get().isStartToken() && token_header.get().getType() == Token.TYPE_DISTRIBUTE_CALLED))) {
			return null;
		} else {
			// System.out.println("releaseToken:" + token_header.get());
			// for (StackTraceElement ste :
			// Thread.currentThread().getStackTrace()) {
			// System.out.println(ste);
			// }
			List<Token> l = token_list.get();
			token_list.set(new ArrayList<Token>());
			return l;
		}
	}

	public String getCurTokenValue() {
		return curTokenValue;
	}

	public Token getPreviousToken() {
		return previousToken;
	}

	public List<Token> getNextToken() {
		return Collections.unmodifiableList(nextToken);
	}

	public int getType() {
		return type;
	}

	public Object getExtraData() {
		return extraData;
	}

	public void setExtraData(Object extraData) {
		this.extraData = extraData;
	}

	public String getName() {
		return name;
	}

	public boolean isDistribute() {
		return isDistribute;
	}

	public boolean isStartToken() {
		return isStartToken;
	}

	public boolean isNotRecord() {
		return notRecord;
	}

	public void setNotRecord(boolean notRecord) {
		this.notRecord = notRecord;
	}

	public void setPreviousToken(Token previousToken) {
		this.previousToken = previousToken;
	}

	public void setNextToken(List<Token> nextToken) {
		this.nextToken = nextToken;
	}

	public long getStartTime() {
		return startTime;
	}

	@Override
	public String toString() {
		String nextTokenStr = "";

		return "token: type=" + this.type + ",isStart=" + this.isStartToken + ",isNotRecord=" + this.isNotRecord() + ",curValue=" + this.curTokenValue
				+ ",nextValue=" + nextTokenStr;
	}
}
